package com.tianji.learning.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.cache.CategoryCache;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CategoryClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.client.search.SearchClient;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CategoryBasicDTO;
import com.tianji.api.dto.course.CourseSearchDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.Vo.LearningNoteAdminPageVo;
import com.tianji.learning.domain.Vo.LearningNoteVO;
import com.tianji.learning.domain.Vo.NoteDetailVo;
import com.tianji.learning.domain.dto.AddNoteDTO;
import cn.hutool.core.bean.BeanUtil;
import com.tianji.common.utils.AssertUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.Vo.LearningNoteVO;
import com.tianji.learning.domain.dto.LearningNoteFromDTO;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.InteractionQuestion;
import com.tianji.learning.domain.po.LearningNote;
import com.tianji.learning.domain.po.NoteUser;
import com.tianji.learning.domain.query.NoteAdminPageQuery;
import com.tianji.learning.domain.query.NoteQuery;
import com.tianji.learning.domain.po.NoteUser;
import com.tianji.learning.mapper.LearningNoteMapper;
import com.tianji.learning.mapper.NoteUserMapper;
import com.tianji.learning.service.ILearningNoteService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.service.INoteUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import java.time.LocalDateTime;

/**
 * <p>
 * 笔记表 服务实现类
 * </p>
 *
 * @author author
 */
@Slf4j
@Service
public class LearningNoteServiceImpl extends ServiceImpl<LearningNoteMapper, LearningNote> implements ILearningNoteService {
    @Autowired
    private SearchClient searchClient;
    @Autowired
    private CourseClient courseClient;
    @Autowired
    private CategoryClient categoryClient;
    @Autowired
    private UserClient userClient;
    @Autowired
    private INoteUserService noteUserService;

    @Override
    public void insertNote(AddNoteDTO dto) {

        Long userId = UserContext.getUser();
        if (userId == null) {
            log.error("用户为空:{}", userId);
            throw new BizIllegalException("网络异常");
        }
        LearningNote learningNote = BeanUtil.copyProperties(dto, LearningNote.class);
        learningNote.setUserId(userId);
        learningNote.setIsPrivate(dto.getIsPrivate());
        learningNote.setVideoTime(dto.getNoteMoment());
        learningNote.setIsCollect(false);
        this.save(learningNote);
        NoteUser noteUser = new NoteUser();
        noteUser.setNoteId(learningNote.getId());
        noteUser.setTargetId(userId);
        noteUserService.save(noteUser);
    }

    @Override
    public PageDTO<LearningNoteAdminPageVo> pageQueryAdmin(NoteAdminPageQuery query) {
        Page<LearningNote> page = query.toMpPage("create_time", false);
        // 查询条件
        LambdaQueryWrapper<LearningNote> queryWrapper = new LambdaQueryWrapper<>();
        // courseName -> courseId
        if (query.getName() != null) {
            List<Long> courseIds = searchClient.queryCoursesIdByName(query.getName());
            if (courseIds.isEmpty()) {
                return null;
            }
            queryWrapper.in(LearningNote::getCourseId, courseIds);
        }


        Boolean hidden = query.getHidden();

        queryWrapper.eq(hidden != null, LearningNote::getIsPrivate, hidden);
        queryWrapper.ge(query.getBeginTime() != null, LearningNote::getCreateTime, query.getBeginTime());
        queryWrapper.le(query.getEndTime() != null, LearningNote::getCreateTime, query.getEndTime());
        this.page(page, queryWrapper);
        List<LearningNote> notes = page.getRecords();
        if (CollUtils.isEmpty(notes)) {
            return PageDTO.empty(page);
        }
        // courseName
        Map<Long, CourseSimpleInfoDTO> courseMap = getCourseMap(notes);
        // chapterName
        Map<Long, CategoryBasicDTO> categoryMap = getCategoryMap();
        // authorName
        Map<Long, UserDTO> userMap = getUserMap(notes);
        Map<Long, CataSimpleInfoDTO> catalogueMap = getCatalogueMap(notes);

        List<LearningNoteAdminPageVo> voList = new ArrayList<>();
        for (LearningNote note : notes) {
            LearningNoteAdminPageVo vo = new LearningNoteAdminPageVo();
            vo.setId(note.getId());
            UserDTO userDTO = userMap.get(note.getUserId());
            vo.setAuthorName(userDTO == null ? "" : userDTO.getName());
            vo.setHidden(note.getIsPrivate());
            vo.setContent(note.getContent());
            CataSimpleInfoDTO chapter = catalogueMap.get(note.getChapterId());
            CataSimpleInfoDTO section = catalogueMap.get(note.getSectionId());
            CategoryBasicDTO sectionDTO = categoryMap.get(note.getSectionId());
            vo.setChapterName(chapter == null ? "" : chapter.getName());
            CourseSimpleInfoDTO courseDTO = courseMap.get(note.getCourseId());
            vo.setCourseName(chapter == null ? "" : chapter.getName());
            vo.setSectionName(section == null ? "" : section.getName());
            vo.setCreateTime(note.getCreateTime());
            vo.setUsedTimes(note.getCollectCount() == null ? 0 : note.getCollectCount());

            voList.add(vo);
        }

        return PageDTO.of(page, voList);
    }

    private Map<Long, CataSimpleInfoDTO> getCatalogueMap(List<LearningNote> questions) {
        Map<Long, CataSimpleInfoDTO> catalogueMap = new HashMap<>();
        Set<Long> sectionIds = new HashSet<>();
        questions.stream().forEach(q -> {
            if (q.getChapterId() != null) {
                sectionIds.add(q.getChapterId());
            }
            if (q.getSectionId() != null) {
                sectionIds.add(q.getSectionId());
            }
        });
        if (CollUtils.isNotEmpty(sectionIds)) {
            List<CataSimpleInfoDTO> catalogues = catalogueClient.batchQueryCatalogue(sectionIds);
            if (CollUtils.isEmpty(catalogues)) {
                log.info("查询不到章节信息：{}", sectionIds);
                throw new BizIllegalException("网络异常");
            }
            catalogueMap = catalogues.stream().collect(Collectors.toMap(CataSimpleInfoDTO::getId, Function.identity()));
        }
        return catalogueMap;
    }

    private Map<Long, UserDTO> getUserMap(List<LearningNote> notes) {
        List<Long> userIds = notes.stream().map(LearningNote::getUserId).distinct().collect(Collectors.toList());
        List<UserDTO> users = userClient.queryUserByIds(userIds);
        if (CollUtils.isEmpty(users)) {
            log.info("查询不到用户信息：{}", userIds);
            throw new BizIllegalException("网络异常");
        }
        Map<Long, UserDTO> userMap = users.stream().collect(Collectors.toMap(UserDTO::getId, Function.identity()));
        return CollUtils.isEmpty(userMap) ? new HashMap<>() : userMap;
    }

    private Map<Long, CategoryBasicDTO> getCategoryMap() {
        List<CategoryBasicDTO> categoryList = categoryClient.getAllOfOneLevel();
        if (CollUtils.isEmpty(categoryList)) {
            log.info("查询不到分类信");
            throw new BizIllegalException("网络异常");
        }
        Map<Long, CategoryBasicDTO> categoryMap = categoryList.stream().collect(Collectors.toMap(CategoryBasicDTO::getId, Function.identity()));
        return CollUtils.isEmpty(categoryMap) ? new HashMap<>() : categoryMap;
    }

    private Map<Long, CourseSimpleInfoDTO> getCourseMap(List<LearningNote> notes) {
        List<Long> courseIds = notes.stream().map(LearningNote::getCourseId).distinct().collect(Collectors.toList());
        List<CourseSimpleInfoDTO> courseList = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(courseList)) {
            log.info("查询不到课程信息：{}", courseIds);
            throw new BizIllegalException("网络异常");
        }
        Map<Long, CourseSimpleInfoDTO> courseMap = courseList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, Function.identity()));
        return CollUtils.isEmpty(courseMap) ? new HashMap<>() : courseMap;
    }

    @Override
    public void updateNote(LearningNoteFromDTO noteDTO) {
        Long userId = UserContext.getUser();
        LearningNote note = getById(noteDTO.getId());
        AssertUtils.isNotNull(note, "笔记不存在");
        AssertUtils.equals(note.getUserId(), userId, "笔记不存在");
        LearningNote learningNote = new LearningNote();
        learningNote.setId(noteDTO.getId());
        if (noteDTO.getIsPrivate() != null) {
            learningNote.setIsPrivate(note.getIsCollect() || noteDTO.getIsPrivate());
        }
        learningNote.setContent(learningNote.getContent());
        updateById(learningNote);
    }

    @Override
    public void deleteNote(Long id) {
        Long userId = UserContext.getUser();
        LearningNote note = getById(id);
        AssertUtils.equals(userId, note.getUserId(), "无法删除他人笔记");
        removeById(id);
    }

    @Override
    public LearningNoteVO getNoteById(Long id) {
        LearningNote learningNote = this.getById(id);
        if (learningNote == null) {
            return null;
        }
        LearningNoteVO vo = BeanUtil.copyProperties(learningNote, LearningNoteVO.class);
        return vo;
    }


    @Override
    public PageDTO<LearningNoteVO> notePage(NoteQuery query) {
        Long userId = UserContext.getUser();
        Page<LearningNoteVO> page = query.toMpPage("create_time", false);
        //不能为隐藏   不是只查自己就不显示隐私笔记
        //全部笔记，不显示私人笔记，和采集的笔记
        //我的笔记展示自己的和采集的
        List<LearningNote> learningNotes = lambdaQuery()
                .eq(query.getOnlyMine(), LearningNote::getUserId, userId)
                .eq(query.getCourseId() != null, LearningNote::getCourseId, query.getCourseId())
                .eq(query.getSectionId() != null, LearningNote::getSectionId, query.getSectionId())
                .eq(!query.getOnlyMine(), LearningNote::getIsPrivate, false)
                .isNull(!query.getOnlyMine(), LearningNote::getTargetId)
                .eq(LearningNote::getIsPrivate, false)
                .list();
        //查询为空直接返回
        if (learningNotes.isEmpty()) {
            return PageDTO.empty(page);
        }
        //调用fegin接口查询全部用户（作者）的信息
        //得到全部用户id
        List<Long> noteIds = learningNotes.stream().map(LearningNote::getId).collect(Collectors.toList());
        List<LearningNote> learningNotes1 = listByIds(noteIds);
        List<Long> noteUserIds = learningNotes1.stream().map(LearningNote::getUserId).collect(Collectors.toList());
        List<Long> lUserIds = learningNotes.stream().map(LearningNote::getUserId).collect(Collectors.toList());
        List<Long> userIds = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(noteUserIds)) {
            userIds.addAll(noteUserIds);
        }
        if (CollectionUtil.isNotEmpty(lUserIds)) {
            userIds.addAll(lUserIds);
        }

        List<UserDTO> userDtoS = userClient.queryUserByIds(userIds);
        if (CollUtils.isEmpty(userDtoS)) {
            log.error("查询用户信息失败");
            throw new BizIllegalException("查询用户信息失败");
        }
        Map<Long, UserDTO> userDtoMap = userDtoS.stream().collect(Collectors.toMap(UserDTO::getId, Function.identity()));
        //填充作者信息
        //如果自己的话就是 作者是自己，如果采集的就是采集人信息
        //查询全部的话，没有隐私，没有采集，都是各自自己的
        List<LearningNoteVO> learningNoteVos = new ArrayList<>(learningNotes.size());
        for (LearningNote learningNote : learningNotes) {
            LearningNoteVO vo = BeanUtil.copyProperties(learningNote, LearningNoteVO.class);
            //c查询的自己的采集来的
            LearningNote learningNotesReal = new LearningNote();
            if (query.getOnlyMine() && learningNote.getId() != null) {
                learningNotesReal = getById(learningNote.getId());
                vo.setIsCollect(true);
            } else {
                learningNotesReal = learningNote;
                vo.setIsCollect(false);
            }
            UserDTO userDTO = userDtoMap.get(learningNotesReal.getUserId());
            vo.setAuthorId(userDTO.getId());
            vo.setAuthorName(userDTO.getUsername());
            vo.setAuthorIcon(userDTO.getIcon());
            learningNoteVos.add(vo);
        }
        return PageDTO.of(page, learningNoteVos);
    }

    @Autowired
    private NoteUserMapper noteUserMapper;

    @Override
    public void getNote(Long id) {
        LearningNote noteDB = getById(id);
        if (noteDB == null) {
            throw new BizIllegalException("笔记不存在");
        }
        LearningNote note = BeanUtil.copyProperties(noteDB, LearningNote.class);
        //1 私密
        note.setIsPrivate(true);
        note.setUpdateTime(LocalDateTime.now());
        note.setUserId(UserContext.getUser());
        //1 有采集
        note.setIsCollect(true);
        //将采集的笔记 插入 笔记表
        save(note);
        //将user id note_id 插入关联表
        NoteUser noteUser = new NoteUser();
        noteUser.setNoteId(note.getId());
        noteUser.setTargetId(noteDB.getUserId());
        noteUserMapper.insert(noteUser);
    }

    /**
     * 取消采集
     *
     * @param id
     */
    @Override
    public void deleteNoted(Long id) {
        Long userId = UserContext.getUser();
        // 查找用户采集的笔记
        QueryWrapper<LearningNote> noteQueryWrapper = new QueryWrapper<>();
        noteQueryWrapper.eq("id", id);
        noteQueryWrapper.eq("user_id", userId);
        noteQueryWrapper.eq("is_collect", 1);

        LearningNote note = getOne(noteQueryWrapper);
        if (note == null) {
            throw new BizIllegalException("采集的笔记不存在或用户无权限取消");
        }

        // 删除采集的笔记记录
        removeById(note.getId());

        // 删除关联表中的记录
        QueryWrapper<NoteUser> noteUserQueryWrapper = new QueryWrapper<>();
        noteUserQueryWrapper.eq("note_id", id);
        noteUserQueryWrapper.eq("target_id", userId);
        noteUserMapper.delete(noteUserQueryWrapper);
    }

    @Autowired
    private CatalogueClient catalogueClient;
    @Autowired
    private CategoryCache categoryCache;

    @Override
    public NoteDetailVo getNoteDetailById(Long id) {
        LearningNote note = this.getById(id);
        NoteDetailVo noteDetailVo = BeanUtil.copyProperties(note, NoteDetailVo.class);
        noteDetailVo.setUsedTimes(note.getCollectCount());
        Long courseId = note.getCourseId();
        Long chapterId = note.getChapterId();
        Long sectionId = note.getSectionId();
        CourseSearchDTO courseDto = courseClient.getSearchInfo(courseId);
        //课程名称
        String courseName = courseDto.getName();
        List<CataSimpleInfoDTO> chapters = catalogueClient.batchQueryCatalogue(Arrays.asList(chapterId));
        Map<Long, CataSimpleInfoDTO> chapterMap = new HashMap<>();
        if (!CollUtils.isEmpty(chapters)) {
            chapterMap = chapters.stream().collect(Collectors.toMap(CataSimpleInfoDTO::getId, Function.identity()));
        }
        //章 和 节
        CataSimpleInfoDTO sectionDto = chapterMap.get(sectionId);
        CataSimpleInfoDTO chapterDto = chapterMap.get(chapterId);
        //多级分类
        String nameByLv3Id = categoryCache.getNameByLv3Id(courseDto.getCategoryIdLv3());
        Long usrId = note.getUserId();
        UserDTO userDTO = userClient.queryUserById(usrId);
        LambdaQueryWrapper<NoteUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(NoteUser::getTargetId, id);
        List<NoteUser> list = noteUserService.list(queryWrapper);
        List<Long> userIds = list.stream().map(NoteUser::getTargetId).collect(Collectors.toList());
        List<UserDTO> userDTOS = userClient.queryUserByIds(userIds);
        List<String> nameList = userDTOS.stream().map(UserDTO::getName).collect(Collectors.toList());
        noteDetailVo.setCourseName(courseName == null ? "" : courseName);
        noteDetailVo.setChapterName(chapterDto.getName() == null ? "" : chapterDto.getName());
        noteDetailVo.setSectionName(sectionDto.getName() == null ? "" : sectionDto.getName());
        noteDetailVo.setCategoryNames(nameByLv3Id == null ? "" : nameByLv3Id);
        noteDetailVo.setAuthorName(userDTO.getName());
        noteDetailVo.setAuthorPhone(userDTO.getCellPhone());
        noteDetailVo.setTargetId(nameList);
        return noteDetailVo;
    }
}
